﻿using System;
using System.Linq;
using System.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Lucene.Linq.Search;

namespace Lucene.Linq.VSTests
{
    [TestClass]
    public class TestIndexer
    {
        #region Nested type: SampleIndexer

        private class SampleIndexer : Indexer<Sample>
        {
            private const int DefaultSleepTime = 100;

            public SampleIndexer(IIndex<Sample> i, int sleepTime = DefaultSleepTime) : base(i)
            {
                Finished = null;
                Disposed = false;
                SleepTime = sleepTime;
            }

            private int SleepTime { get; set; }

            public DateTime? Finished { get; private set; }

            public bool Disposed { get; private set; }

            public override void Run()
            {
                Thread.Sleep(SleepTime);
                Finished = DateTime.Now;
                Stopped = true;
            }

            protected override void Dispose(bool disposing)
            {
                base.Dispose(disposing);
                Disposed = true;
            }
        }

        #endregion

        #region Ctors

        [TestMethod, ExpectedException(typeof (ArgumentNullException))]
        public void Construct_Throws_With_Null_Index()
        {
            var i1 = new SequentialIndexer<Sample>(null);

            var i2 = new ParallelIndexer<Sample>(null);

            var i3 = new ParallelIndexer<Sample>(null);
        }

        [TestMethod]
        public void Construct()
        {
            using (var pi = new ParallelIndexer<Sample>(new Index<Sample>()))
            {
                Assert.IsNotNull(pi.Index);
            }


            using (var si = new SequentialIndexer<Sample>(new Index<Sample>()))
            {
                Assert.IsNotNull(si.Index);
            }
        }

        #endregion

        #region Disposal

        [TestMethod]
        public void Indexers_implement_IDisposable()
        {
            Type par = typeof (Indexer<Sample>);

            Type idis = typeof (IDisposable);
            Assert.IsTrue(idis.IsAssignableFrom(par));
        }

        #endregion

        #region Child Disposal

        private void DisposeAndCheckDisposedChildren(Indexer<Sample> root)
        {
            root.AddChild(new SampleIndexer(root.Index));
            root.AddChild(new SampleIndexer(root.Index));

            root.Dispose();

            Assert.IsTrue(root.Children.Cast<SampleIndexer>().All(pi => pi.Disposed), "All children must be disposed of");
        }

        [TestMethod]
        public void Composite_indexer_Disposal_Should_Dispose_Of_Children()
        {
            DisposeAndCheckDisposedChildren(new ParallelIndexer<Sample>(new Index<Sample>()));
            DisposeAndCheckDisposedChildren(new SequentialIndexer<Sample>(new Index<Sample>()));
        }

        #endregion

        #region Sequential

        [TestMethod]
        public void Sequential_Executes_Children_In_Order()
        {
            // set up
            using (var idx = new Index<Sample>())
            {
                var first = new SampleIndexer(idx);
                Assert.IsNotNull(first.Index);

                var second = new SampleIndexer(idx);
                Assert.IsNotNull(second.Index);

                var third = new SampleIndexer(idx);
                Assert.IsNotNull(third.Index);

                using (var si = new SequentialIndexer<Sample>(idx))
                {
                    Assert.IsNotNull(si.Index);

                    si.AddChild(first);
                    si.AddChild(second);
                    si.AddChild(third);


                    // execute
                    si.Run();


                    // verify
                    Assert.IsTrue(si.Stopped);
                    Assert.IsTrue(si.Children.All(i => i.Stopped), "All children should be stopped");
                    Assert.IsTrue(first.Finished < second.Finished, "First shouldve finished b4 second");
                    Assert.IsTrue(second.Finished < third.Finished, "Second shouldve finished b4 third");
                }
            }
        }

        #endregion

        #region Parallel

        [TestMethod]
        public void Parallel_Executes_Children_In_Any_Order()
        {
            // set up
            using (var idx = new Index<Sample>())
            {
                var first = new SampleIndexer(idx, 300);
                Assert.IsNotNull(first.Index);

                var second = new SampleIndexer(idx, 200);
                Assert.IsNotNull(second.Index);

                var third = new SampleIndexer(idx, 100);
                Assert.IsNotNull(third.Index);

                using (var parallelIndexer = new ParallelIndexer<Sample>(idx))
                {
                    Assert.IsNotNull(parallelIndexer.Index);

                    parallelIndexer.AddChild(first);
                    parallelIndexer.AddChild(second);
                    parallelIndexer.AddChild(third);


                    // execute
                    parallelIndexer.Run();

                    // verify
                    Assert.IsTrue(first.Finished > second.Finished, "First shouldve finished after second");
                    Assert.IsTrue(second.Finished > third.Finished, "Second shouldve finished after third");
                }
            }
        }

        #endregion
    }
}